#! /bin/bash

#set -x

. usage.sh

# Default qmake setup (for GUI compilation). This requires Qt4 or Qt5 (Qt5 is
# preferred). We try to locate the qmake executable in some common locations
# here. If this doesn't work out then you can also set the QMAKE environment
# variable explicitly, or use one of the -qt4 and -qt5 options below.
[ -z "$QMAKE" ] && QMAKE=$(which qmake-qt5 || which /opt/local/libexec/qt5/bin/qmake || which qmake-qt4 || which /opt/local/libexec/qt4/bin/qmake || echo qmake)

# Where the Faust includes live. We assume that this is under the prefix of
# whatever Faust binary 'which' locates. You can also specify this explicitly
# by setting the FAUSTINC environment variable accordingly.
[ -z "$FAUSTINC" ] && FAUSTINC=$(which faust 2>/dev/null | sed -e 's?/bin/faust?/include/faust?')

# Where our own Faust library files are. This may be under a different prefix
# or not installed anywhere. We try 'ls' on lv2ui.cpp in some common locations
# here, and fall back to the current directory if the file isn't found, so
# that you can run the script from the faust-lv2 source directory. You can
# also specify this explicitly by setting the FAUSTLIB environment variable
# accordingly.
[ -z "$FAUSTLIB" ] && FAUSTLIB=$(dirname "$((ls -f /usr/share/faust/lv2ui.cpp /usr/local/share/faust/lv2ui.cpp /opt/local/share/faust/lv2ui.cpp "$PWD/lv2ui.cpp" 2>/dev/null)|tail -1)")
[ -z "$FAUSTLIB" ] && FAUSTLIB="$PWD"

# defaults (these can be changed with the options listed below)
URI_PREFIX=https://faustlv2.bitbucket.io
FAUST_META=1
FAUST_MIDICC=1
FAUST_MTS=1
FAUST_UI=0
VOICE_CTRLS=1
NVOICES=-1

KEEP="no"
STYLE=""

PROCARCH="-fPIC"
dllext=".so"
CXXFLAGS+=" -std=c++11 -fvisibility=hidden -O3 "  # WARNING: don't modify this setting
echo $CXXFLAGS

# Darwin specifics
if [[ $(uname) == Darwin ]]; then
    dllext=".dylib"
    CXXFLAGS+=" -I/opt/local/include"
fi

# dispatch command arguments
for ((i=1; i<$#+1; i++)); do
    p=${!i}
    if [ $p = "-help" ] || [ $p = "-h" ]; then
        usage faust2lv2 "[options ...] <file.dsp>"
        require QT
        echo  "Compiles Faust programs to lv2 plugins"
        option
        option -dyn-manifest "use dynamic manifests (requires LV2 host support)."
        option -gui "build the plugin GUI (requires LV2 UI host support)."
        option -keep "retain the build directory."
        option -nometa "ignore metadata (author information etc.) from the Faust source"
        option -nomidicc "plugin doesn't process MIDI control data."
        option -notuning "disable the tuning control (instruments only)."
        option -novoicectrls "no extra polyphony/tuning controls on GUI (instruments only)"
        option "-nvoices N" "number of synth voices (instruments only; arg must be an integer)"
        option -osx "-osx to compile on OSX"
        option "-qt4, -qt5" "select the GUI toolkit (requires Qt4/5; implies -gui)"
        option "-style S" "select the stylesheet (arg must be Default, Blue, Grey or Salmon)."
        cat <<EOF
Environment variables:
  FAUSTINC: specify the location of the Faust include directory
    Default: $FAUSTINC
  FAUSTLIB: specify the location of the Faust LV2 library files
    Default: $FAUSTLIB
  QMAKE: specify the location of the qmake binary
    Default: $QMAKE
EOF
    exit 0
    elif [ $p = "-omp" ]; then
        : ignore
    elif [ $p = "-icc" ]; then
        CXX=icpc
        CXXFLAGS="-O3 -xHost -ftz -fno-alias -fp-model fast=2"
    elif [ $p = "-osc" ]; then
        # not implemented for now
        OSCDEFS="DEFINES += OSCCTRL"
        OSCLIBS="-lOSCFaust"
    elif [ $p = "-httpd" ]; then
        # not implemented for now
        HTTPDEFS="DEFINES += HTTPCTRL"
        HTTPLIBS="-lHTTPDFaust"
        HTTPLIBS1=`pkg-config --cflags --libs libmicrohttpd`
    elif [ $p = "-qrcode" ]; then # requires -httpd
        QRDEFS="DEFINES += QRCODECTRL"
    elif [ $p = "-nometa" ]; then
        FAUST_META=0
    elif [ $p = "-nomidicc" ]; then
        FAUST_MIDICC=0
    elif [ $p = "-notuning" ]; then
        FAUST_MTS=0
    elif [ $p = "-novoicectrls" ]; then
        VOICE_CTRLS=0
    elif [ $p = "-gui" ]; then
        FAUST_UI=1
        plugin_gui=yes
    elif [ $p = "-qt4" ]; then
        FAUST_UI=1
        plugin_gui=yes
        QMAKE=$(which qmake-qt4 || which /opt/local/libexec/qt4/bin/qmake || echo qmake-qt4)
    elif [ $p = "-qt5" ]; then
        FAUST_UI=1
        plugin_gui=yes
        QMAKE=$(which qmake-qt5 || which /opt/local/libexec/qt5/bin/qmake || echo qmake-qt5)
    elif [ $p = "-dyn-manifest" ]; then
        dyn_manifest=yes
    elif [ $p = "-uri-prefix" ]; then
        (( i++ ))
        URI_PREFIX=${!i}
    elif [ $p = "-nvoices" ]; then
        (( i++ ))
        NVOICES=${!i}
    elif [ $p = "-arch32" ]; then
        PROCARCH="-m32 -L/usr/lib32"
    elif [ $p = "-arch64" ]; then
        PROCARCH="-m64 -fPIC"
    elif [ $p = "-keep" ]; then
        KEEP="yes"
    elif [ $p = "-style" ]; then
        (( i++ ))
        STYLE=${!i}
    elif [ ${p:0:1} = "-" ]; then
        OPTIONS="$OPTIONS $p"
    elif [[ -f "$p" ]] && [ ${p: -4} == ".dsp" ]; then
        FILES="$FILES $p"
    else
        OPTIONS="$OPTIONS $p"
    fi
done

FILES=($FILES)
if [ ${#FILES[@]} = 0 ]; then
    echo "$0: no filename specified" >&2
    exit 1
elif [ ${#FILES[@]} -gt 1 ]; then
    echo "$0: multiple filenames specified" >&2
    exit 1
fi

# Check to see whether the required include and library files are where we
# expect them, and bail out with an error message otherwise.
if [ ! -f "$FAUSTINC/gui/QTUI.h" ]; then echo "$0: faust include files not found" >&2; exit 1; fi
if [ ! -f "$FAUSTLIB/lv2ui.cpp" ]; then echo "$0: faust-lv2 library files not found" >&2; exit 1; fi

# Determine the Qt version so that we can edit the manifests accordingly.
QTVERSION=$($QMAKE -v 2>/dev/null | tail -1 | sed 's/.*Qt version \([0-9]\).*/\1/')

arch=lv2.cpp
archui=lv2ui.cpp
dspname=${FILES[0]}
SRCDIR=$(dirname "$dspname")
ABSDIR=$(cd $SRCDIR && pwd)
CURDIR=$(pwd)

clsname=`basename "$dspname" .dsp`
cppname="$clsname.cpp"
soname="$clsname$dllext"
uicppname="${clsname}ui.cpp"
uisoname="${clsname}ui$dllext"
lv2name="$clsname.lv2"
tmpdir=`mktemp -d /tmp/faust2lv2.XXXXXX`
uitmpdir=$tmpdir/${clsname}ui.src

RESOURCES=
STYLE_CXXFLAGS=
if [ -n "$STYLE" ]; then
    RESOURCES="RESOURCES+=$FAUSTINC/gui/Styles/$STYLE.qrc"
    STYLE_CXXFLAGS="QMAKE_CXXFLAGS+=-DSTYLE=\"$STYLE\""
fi

if [ -z ${CXX} ]; then
CXX=g++
echo "Using ${CXX}"
else
echo "compiler overridden: using ${CXX}"
fi

HOST_CXX=g++

CPPFLAGS="-DPLUGIN_URI=\"$URI_PREFIX/$clsname\" -DFAUST_META=$FAUST_META -DFAUST_MIDICC=$FAUST_MIDICC -DFAUST_MTS=$FAUST_MTS -DFAUST_UI=$FAUST_UI -DVOICE_CTRLS=$VOICE_CTRLS"
if [ $NVOICES -ge 0 ]; then
CPPFLAGS="$CPPFLAGS -DNVOICES=$NVOICES"
fi

# Create the temp directory and the bundle directory inside it.
mkdir -p $tmpdir/$lv2name
#trap "echo $0: compile error, intermediate files left in $tmpdir >&2" EXIT
# Compile the Faust module.
faust -i -a "$FAUSTLIB/$arch" -cn "$clsname" $OPTIONS "$dspname" -o "$tmpdir/$cppname" || exit 1
$CXX -shared $CXXFLAGS -DDLLEXT="\"$dllext\"" $FAUSTTOOLSFLAGS $PROCARCH -I"$ABSDIR" $CPPFLAGS "$tmpdir/$cppname" -o "$tmpdir/$lv2name/$soname" || exit 1
if [ -n "$plugin_gui" ]; then
# Compile the UI module.
mkdir -p $uitmpdir
faust -i -a  "$FAUSTLIB/$archui" -cn "$clsname" $OPTIONS "$dspname" -o "$uitmpdir/$uicppname" || exit 1
(
    cd $uitmpdir
    $QMAKE -project -t lib -o ${clsname}ui.pro "CONFIG += gui plugin no_plugin_name_prefix warn_off" "QT += widgets printsupport network" "INCLUDEPATH+=$ABSDIR" "INCLUDEPATH+=$CURDIR" "INCLUDEPATH+=$FAUSTLIB" "INCLUDEPATH+=$FAUSTINC" "QMAKE_CXXFLAGS=-std=c++11 $(echo $CPPFLAGS|sed -e 's/"/\\\\\\"/g')" $STYLE_CXXFLAGS "LIBS+=$ARCHLIB $OSCLIBS $HTTPLIBS $HTTPLIBS1" "HEADERS+=$FAUSTLIB/lv2qtgui.h $FAUSTINC/gui/QTUI.h" $RESOURCES "$OSCDEFS" "$HTTPDEFS" "$QRDEFS"
    $QMAKE *.pro
    make || exit 1
    mv $uisoname "$tmpdir/$lv2name"
) > /dev/null || exit 1
fi
# Generate the manifest. There are four different variations of the manifest,
# depending on whether dynamic manifests and the plugin gui is enabled or not.
if [ -n "$dyn_manifest" ]; then
# Use a dynamic manifest.
if [ -n "$plugin_gui" ]; then
sed -e"s?@name@?$clsname?g" -e"s?@uri@?$URI_PREFIX/$clsname?g" -e"s?@dllext@?$dllext?g" -e "s?ui:Qt5UI?ui:Qt${QTVERSION}UI?g" > "$tmpdir/$lv2name/manifest.ttl" <<EOF

########## @uri@ ##########

@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .
@prefix dman: <http://lv2plug.in/ns/ext/dynmanifest#> .

<@uri@/manifest>
    lv2:binary <@name@@dllext@> ;
    a dman:DynManifest .

<@uri@ui>
    a ui:Qt5UI ;
    ui:binary <@name@ui@dllext@> .

# Here's how you can declare the category of the plugin. (For instruments,
# the lv2:InstrumentPlugin type will be added automatically.) See
# http://lv2plug.in/ns/lv2core/ for a list of known plugin classes.

# <@uri@> a lv2:FilterPlugin .

# You might also want to set the license and author information below.
# NOTE: This isn't normally necessary if you declared the corresponding
# information as metadata in the Faust source of the plugin. The standard
# author, license and description fields in the Faust source are automagically
# included in the generated LV2 manifest.

# <@uri@>
#     doap:license <http://opensource.org/licenses/isc> ;
#     doap:maintainer [
#         foaf:name "Your Name Here" ;
#         foaf:homepage <http://somewhere.org/> ;
#         foaf:mbox <mailto:your@mail.here> ;
#     ] .
EOF
else
sed -e"s?@name@?$clsname?g" -e"s?@uri@?$URI_PREFIX/$clsname?g" -e"s?@dllext@?$dllext?g" > "$tmpdir/$lv2name/manifest.ttl" <<EOF

########## @uri@ ##########

@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix dman: <http://lv2plug.in/ns/ext/dynmanifest#> .

<@uri@/manifest>
    lv2:binary <@name@@dllext@> ;
    a dman:DynManifest .

# Here's how you can declare the category of the plugin. (For instruments,
# the lv2:InstrumentPlugin type will be added automatically.) See
# http://lv2plug.in/ns/lv2core/ for a list of known plugin classes.

# <@uri@> a lv2:FilterPlugin .

# You might also want to set the license and author information below.
# NOTE: This isn't normally necessary if you declared the corresponding
# information as metadata in the Faust source of the plugin. The standard
# author, license and description fields in the Faust source are automagically
# included in the generated LV2 manifest.

# <@uri@>
#     doap:license <http://opensource.org/licenses/isc> ;
#     doap:maintainer [
#         foaf:name "Your Name Here" ;
#         foaf:homepage <http://somewhere.org/> ;
#         foaf:mbox <mailto:your@mail.here> ;
#     ] .
EOF
fi
else
# Use a static manifest.
if [ -n "$plugin_gui" ]; then
sed -e"s?@name@?$clsname?g" -e"s?@uri@?$URI_PREFIX/$clsname?g" -e"s?@dllext@?$dllext?g" -e "s?ui:Qt5UI?ui:Qt${QTVERSION}UI?g" > "$tmpdir/$lv2name/manifest.ttl" <<EOF

########## @uri@ ##########

@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<@uri@>
    a lv2:Plugin ;
    lv2:binary <@name@@dllext@> ;
    rdfs:seeAlso <@name@.ttl> .

<@uri@ui>
    a ui:Qt5UI ;
    ui:binary <@name@ui@dllext@> .

# Here's how you can declare the category of the plugin. (For instruments,
# the lv2:InstrumentPlugin type will be added automatically.) See
# http://lv2plug.in/ns/lv2core/ for a list of known plugin classes.

# <@uri@> a lv2:FilterPlugin .

# You might also want to set the license and author information below.
# NOTE: This isn't normally necessary if you declared the corresponding
# information as metadata in the Faust source of the plugin. The standard
# author, license and description fields in the Faust source are automagically
# included in the generated LV2 manifest.

# <@uri@>
#     doap:license <http://opensource.org/licenses/isc> ;
#     doap:maintainer [
#         foaf:name "Your Name Here" ;
#         foaf:homepage <http://somewhere.org/> ;
#         foaf:mbox <mailto:your@mail.here> ;
#     ] .
EOF
else
sed -e"s?@name@?$clsname?g" -e"s?@uri@?$URI_PREFIX/$clsname?g" -e"s?@dllext@?$dllext?g" > "$tmpdir/$lv2name/manifest.ttl" <<EOF

########## @uri@ ##########

@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<@uri@>
    a lv2:Plugin ;
    lv2:binary <@name@@dllext@> ;
    rdfs:seeAlso <@name@.ttl> .

# Here's how you can declare the category of the plugin. (For instruments,
# the lv2:InstrumentPlugin type will be added automatically.) See
# http://lv2plug.in/ns/lv2core/ for a list of known plugin classes.

# <@uri@> a lv2:FilterPlugin .

# You might also want to set the license and author information below.
# NOTE: This isn't normally necessary if you declared the corresponding
# information as metadata in the Faust source of the plugin. The standard
# author, license and description fields in the Faust source are automagically
# included in the generated LV2 manifest.

# <@uri@>
#     doap:license <http://opensource.org/licenses/isc> ;
#     doap:maintainer [
#         foaf:name "Your Name Here" ;
#         foaf:homepage <http://somewhere.org/> ;
#         foaf:mbox <mailto:your@mail.here> ;
#     ] .
EOF
fi
# This compiles the plugin to an executable which is run to generate the
# plugin-specific part of the manifest.
$HOST_CXX $CXXFLAGS -DDLLEXT="\"$dllext\"" $FAUSTTOOLSFLAGS -I"$ABSDIR" $CPPFLAGS "$tmpdir/$cppname" -o "$tmpdir/$clsname" || exit 1
"$tmpdir/$clsname" > "$tmpdir/$lv2name/$clsname.ttl"
rm -f "$tmpdir/$clsname"
fi
#trap - EXIT

# copy down the bundle
rm -rf "$SRCDIR/$lv2name"
cp -r "$tmpdir/$lv2name" "$SRCDIR"
if [[ $KEEP == yes ]]; then
    # keep the build directory
    rm -rf "$SRCDIR/$clsname"
    mv $tmpdir "$SRCDIR/$clsname"
else
    # Clean up.
    rm -rf $tmpdir
fi
# Print the name of the generated bundle zip file.
echo "LV2 plugin generated at $SRCDIR/$lv2name;"
